# Copyright 2014 Sean Burford <vk2ete@vk2ete.net>.  All Rights Reserved.
import logging
import urllib

import easyphpcalendar
import event
import ical_ds

from google.appengine.api import urlfetch


class Error(Exception):

  def __init__(self, status_code, desc):
    super(Error, self).__init__()
    self.status_code = status_code
    self.desc = desc

  def __str__(self):
    return '%s: %s' % (self.status_code, self.desc)

  def __repr__(self):
    return '<Error %s>' % (self)


class FeedInfo(object):
  def __init__(self, feed_name, url, params, categories):
    self.feed_name = feed_name
    self.url = url
    self.params = params
    self.categories = categories
    self.infos = []
    self.errors = []


class Feed(object):
  _FEEDS = {
      'wia': FeedInfo('wia',
                      'http://www.wia.org.au/newsevents/events/index.php',
                      {'showCat': '', 'state1': 'ALL'},
                      {'S21': 'Training', 'S22': 'Hamfest',
                       'S23': 'Assessment', 'S24': 'Club Meeting',
                       'S221': 'Field Day', 'S222': 'Contest',
                       'S223': 'WIA Event'}),
  }

  def __init__(self, feed_name, debug=False):
    self.feed_name = feed_name
    self._debug = debug

    self.feed_info = self._FEEDS.get(feed_name)
    if not self.feed_info:
      raise ValueError('unknown feed %r' % (feed_name,))

  def _URLFetch(self, year, month):
    form_data = dict(self.feed_info.params)
    if year is not None:
      form_data['yr'] = '%04d' % year
    if month is not None:
      form_data['mo'] = '%02d' % month

    url = '%s?%s' % (self.feed_info.url, urllib.urlencode(form_data))

    if self._debug:
      fn = 'testdata/index.php?showCat=&state1=ALL&yr=%04d&mo=%02d' % (
          year, month)
      logging.info('processing %s', fn)
      fp = open(fn, 'rb')
      buf = fp.read()
      fp.close()
    else:
      logging.info('fetching %r', url)
      try:
        result = urlfetch.fetch(url)
      except urlfetch.Error as e:
        raise Error(500, 'fetch error: %s' % e)
      if result.status_code != 200:
        raise Error(result.status_code, 'error fetching %s' % (url,))
      buf = result.content

    return url, unicode(buf, encoding='ascii', errors='replace')

  def _ParsePage(self, url, buf):
    parser = easyphpcalendar.EasyPHPCalendarMonthParser(url)
    parser.feed(buf)
    return parser.events

  def CategoryToText(self, cat):
    if not cat:
      return ''
    return self.feed_info.categories.get(cat, cat)

  @classmethod
  def GetFeedInfos(cls):
    return cls._FEEDS

  def UpdateEvents(self, year, month, existing_events=()):
    url, buf = self._URLFetch(year, month)
    events = self._ParsePage(url, buf)

    return_events = []
    ical_events = []
    for evt in events:
      if not evt.subject:
        continue
      for candidate in existing_events:
        if (candidate.timezone == evt.timezone and
            candidate.callsign == evt.callsign and
            candidate.subject == evt.subject and
            candidate.date_start == evt.date_start and
            candidate.date_end == evt.date_end and
            candidate.description == evt.description and
            candidate.comment == evt.comment):
          break
      else:
        ical_events.append(ical_ds.CalendarEventToDS(self.feed_name, evt))
        return_events.append(evt)

    keys = ical_ds.Put(ical_events)
    if keys:
      logging.info('update %s %04d %02d %d events',
                   self.feed_name, year, month, len(keys))

    return return_events

  def FetchEvents(self, date_range, query):
    location_clues = ical_ds.GetLocationClues(self.feed_name)
    events = ical_ds.Find(self.feed_name, date_range[0], date_range[1])

    found_events = []
    patch_count = 0
    for evt in events or ():
      if ((not query.get('category')) or (not evt.categories) or
          (query['category'].intersection(evt.categories))):
        patch_count += event.GuessLocation(evt, location_clues)
        if ((not query.get('callsign')) or (not evt.callsign) or
            (evt.callsign in query['callsign'])):
          found_events.append(evt)

    logging.info('found %d matching %s events (%d location fixups) %s - %s',
                 len(found_events), self.feed_name, patch_count,
                 date_range[0], date_range[1])

    return found_events

